﻿using Manox.Libs.Networking;
using System;
using System.Collections.Generic;
using System.Reflection;

namespace Manox.Libs.ODB
{
    /// <summary>
    /// Object database object.
    /// </summary>
    [Serializable]
    public class ObjectDatabaseObject : ObjectDatabaseItem
    {
        /// <summary>Properties.</summary>
        internal List<ObjectDatabasePropertyBase> Properties;
        

        /// <summary>
        /// Constructor.
        /// </summary>
        public ObjectDatabaseObject()
        {
            this.Properties = new List<ObjectDatabasePropertyBase>();

            this.Init();

            this.InitializeProperties();
        }

        /// <summary>
        /// Init.
        /// </summary>
        public virtual void Init()
        {

        }

        /// <summary>
        /// Method initialize propertiues.
        /// </summary>
        public void InitializeProperties()
        {
            lock (this.Properties)
            {
                this.Properties.Clear();

                FieldInfo[] fields = this.GetType().GetFields();

                for (int i = fields.Length - 1; i >= 0; i--)
                {
                    object propertyValue = fields[i].GetValue(this);

                    if (propertyValue is ObjectDatabasePropertyBase)
                    {
                        (propertyValue as ObjectDatabasePropertyBase).Name = fields[i].Name;

                        /// Register value changed event.
                        (propertyValue as ObjectDatabasePropertyBase).ValueChanged += this.CallPropertyChangedEvent;

                        this.Properties.Add(propertyValue as ObjectDatabasePropertyBase);
                    }
                }
            }
        }

        /// <summary>
        /// Call property changed event.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="eventArgs"></param>
        public void CallPropertyChangedEvent(object sender, ObjectDatabasePropertyValueChangedEventArgs eventArgs)
        {
            ObjectDatabaseObjectPropertyValueChangedEventArgs objectEventArgs = 
                new ObjectDatabaseObjectPropertyValueChangedEventArgs();

            objectEventArgs.DatabaseObjectId = this.Id;
            objectEventArgs.PropertyEventArgs = eventArgs;

            this.CallDataChangedEvent(sender, objectEventArgs);
        }

        /// <summary>
        /// Initialize in ODB.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="odbManager"></param>
        public override void InitializeInODB(object sender, ObjectDatabaseManager odbManager)
        {
            FieldInfo[] fields = this.GetType().GetFields();

            for (int i = fields.Length - 1; i >= 0; i--)
            {
                object fieldValue = fields[i].GetValue(this);

                if (fieldValue is ObjectDatabasePropertyBase)
                {
                    ObjectDatabasePropertyBase propertyBase = (fieldValue as ObjectDatabasePropertyBase);

                    if (propertyBase.GetObject() is ObjectDatabaseItem)
                    {
                        ObjectDatabaseItem item = propertyBase.GetObject() as ObjectDatabaseItem;

                        if (item.Id == Guid.Empty)                            
                            odbManager.AddObject(sender, item);
                    }
                        
                    propertyBase.ForceCallValueChangedEvent(sender);
                }               
            }
        }

        /// <summary>
        /// Initializing EventArgs.
        /// </summary>
        /// <returns></returns>
        public override List<ObjectDatabaseEventArgs> InitializingEventArgs()
        {
            List<ObjectDatabaseEventArgs> output = new List<ObjectDatabaseEventArgs>();

            FieldInfo[] fields = this.GetType().GetFields();

            for (int i = fields.Length - 1; i >= 0; i--)
            {
                object fieldValue = fields[i].GetValue(this);

                if (fieldValue is ObjectDatabasePropertyBase)
                {
                    ObjectDatabasePropertyBase propertyBase = (fieldValue as ObjectDatabasePropertyBase);

                    output.Add(propertyBase.CreateEventArgs(this));
                }
            }

            return output;
        }

        /// <summary>
        /// Get property.
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public ObjectDatabasePropertyBase GetProperty(string name)
        {
            foreach (ObjectDatabasePropertyBase property in this.Properties)
                if (property.Name == name)
                    return property;

            return null;
        }

        /// <summary>
        /// Is finished.
        /// </summary>
        /// <returns></returns>
        public override bool IsFinished()
        {
            foreach (ObjectDatabasePropertyBase property in this.Properties)
                if (property.GetObject() is ObjectDatabaseItemProxy)
                    return false;

            return true;
        }
    }

    /// <summary>
    /// Object database Object property value changed.
    /// </summary>
    public class ObjectDatabaseObjectPropertyValueChangedEventArgs : ObjectDatabaseEventArgs
    {
        /// <summary>Database object id..</summary>
        public Guid DatabaseObjectId;
        /// <summary>Property value changed event args.</summary>
        public ObjectDatabasePropertyValueChangedEventArgs PropertyEventArgs;


#if UNITY_NETWORK || STEAM_NETWORK
        /// <summary>
        /// Serialize.
        /// </summary>
        /// <param name="writer"></param>
        public override void Serialize(SerializeWriter writer)
        {
            base.Serialize(writer);

            writer.Write(this.DatabaseObjectId);

            this.PropertyEventArgs.Serialize(writer);
        }

        /// <summary>
        /// Deserialize.
        /// </summary>
        /// <param name="reader"></param>
        public override void Deserialize(SerializeReader reader)
        {
            base.Deserialize(reader);

            this.DatabaseObjectId = reader.ReadGuid();

            this.PropertyEventArgs = new ObjectDatabasePropertyValueChangedEventArgs();
            this.PropertyEventArgs.Deserialize(reader);
        }
#endif                    
    }
}
